//#include "linux/linux_util.h"

#include <dirent.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <sys/timeb.h>
#include <regex>
#include <experimental/filesystem>  // filesystem
#include <fstream>

//#include "file/file_util.h"
//#include "xaglog/xaglog.hpp"
#include "common.h"
//#include "system/system_modules.h"

namespace xag {
namespace xos {
namespace utils {
namespace LinuxUtil {

namespace fs = std::experimental::filesystem;

using std::string;

int GetLinuxRunTime() {
  FILE *fp = ::fopen("/proc/uptime", "r");

  if (fp == nullptr) {
    SPDLOG_ERROR("fopen /proc/uptime error");
    return -1;
  } else {
    char buf[1024]{0};
    string time_value;

    // 循环读取文件的每一行数据
    while (::fgets(buf, sizeof(buf), fp) != nullptr) {
      time_value += buf;
    }

    ::fclose(fp);
    fp = nullptr;

    // 转换为整数
    return std::stoi(time_value);
  }
}

xag::xos::system::SysCpuState GetSysCpuState(CpuInfo_t *cpu_info_last) {
  CpuInfo_t cpu_info;
  FILE *file_fd = NULL;
  char buf[1024]{0};

  xag::xos::system::SysCpuState sys_cpu_state;

  file_fd = ::fopen("/proc/stat", "r");
  if (file_fd == NULL) {
    SPDLOG_ERROR("fopen /proc/stat error");
    return sys_cpu_state;
  }

  xag::xos::deamon::Common::read_uptime(&cpu_info.uptime_cs);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s%d%d%d%d%d%d%d", cpu_info.name, &cpu_info.user,
           &cpu_info.nice, &cpu_info.system, &cpu_info.idle, &cpu_info.iowait,
           &cpu_info.irq, &cpu_info.softirq);

  while (fgets(buf, sizeof(buf), file_fd) != NULL) {

    if (!strncmp(buf, "ctxt ", 5)) {
      /* Read number of context switches */
      sscanf(buf + 5, "%llu", &cpu_info.ctxt);
    }
    else if (!strncmp(buf, "intr ", 5)) {
      /* Read number of processes created since system boot */
      sscanf(buf + 5, "%lu", &cpu_info.intr);
    }
    else if (!strncmp(buf, "procs_blocked ", 14)) {
      /* Read number of processes blocked */
      sscanf(buf + 14, "%llu", &cpu_info.blocked);
    }
  }

  ::fclose(file_fd);
  file_fd = NULL;

  if (cpu_info_last->user == 0) {
    *cpu_info_last = cpu_info;
    return sys_cpu_state;
  }

  int64_t all_now_cpu_info = cpu_info.user + cpu_info.nice + cpu_info.system +
                             cpu_info.idle + cpu_info.iowait + cpu_info.irq +
                             cpu_info.softirq;
                             
  int64_t all_last_cpu_info = cpu_info_last->user + cpu_info_last->nice +
                              cpu_info_last->system + cpu_info_last->idle +
                              cpu_info_last->iowait + cpu_info_last->irq +
                              cpu_info_last->softirq;

  int64_t idle = cpu_info.idle - cpu_info_last->idle;
  int64_t total = all_now_cpu_info - all_last_cpu_info;

  sys_cpu_state.cpu_utilization = (double)(total - idle) * 100 / total;
  sys_cpu_state.cpu_temperature = GetCpuTemperature();
  sys_cpu_state.user = (double)(cpu_info.user - cpu_info_last->user) * 100 / total;    
  sys_cpu_state.nice = (double)(cpu_info.nice - cpu_info_last->nice) * 100 / total;    
  sys_cpu_state.system = (double)(cpu_info.system - cpu_info_last->system) * 100 / total;  
  sys_cpu_state.idle = (double)(cpu_info.idle - cpu_info_last->idle) * 100 / total;    
  sys_cpu_state.iowait = (double)(cpu_info.iowait - cpu_info_last->iowait) * 100 / total;
  sys_cpu_state.irq = (double)(cpu_info.irq - cpu_info_last->irq) * 100 / total;
  sys_cpu_state.softirq = (double)(cpu_info.softirq - cpu_info_last->softirq) * 100 / total;

  auto itv = xag::xos::deamon::Common::get_interval(cpu_info_last->uptime_cs, cpu_info.uptime_cs);
  sys_cpu_state.ctxt =  ((double) (cpu_info.ctxt - cpu_info_last->ctxt)) / (itv) * 100;
  sys_cpu_state.intr =  ((double) (cpu_info.intr - cpu_info_last->intr)) / (itv) * 100;
  sys_cpu_state.blocked = cpu_info.blocked;
  
  *cpu_info_last = cpu_info;

  return sys_cpu_state;
}

double GetCpuTemperature() {
  FILE *file_fd = NULL;
  CpuTemp_t cpu_temp;
  char buf[1024]{0};

  file_fd = ::fopen("/sys/class/thermal/thermal_zone0/temp", "r");
  if (file_fd == NULL) {
    SPDLOG_ERROR("fopen cpu_temp_path error");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%u", &cpu_temp.temp);
  ::fclose(file_fd);
  file_fd = NULL;

  return (double)(cpu_temp.temp / 1000.0);
}

double GetRamUtilization() 
{
  FILE *file_fd = NULL;
  Memory_t memory;
  char buf[1024]{0};

  file_fd = ::fopen("/proc/meminfo", "r");
  if (file_fd == NULL) {
    ::perror("fopen /proc/meminfo error");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s %ld ", memory.name, &memory.total);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s %ld ", memory.name2, &memory.free);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s %ld ", memory.name3, &memory.available);

  ::fclose(file_fd);
  file_fd = NULL;

  return (double)(100.0 * (memory.total - memory.available)) / (memory.total);
}

double GetRomUtilization() 
{
  struct statfs diskInfo;
  if (::statfs("/data", &diskInfo) < 0) {
    ::perror("statfs error");
  }

  // struct statfs {
  //   long f_type;    /* 文件系统类型  */
  //   long f_bsize;   /* 经过优化的传输块大小  */
  //   long f_blocks;  /* 文件系统数据块总数 */
  //   long f_bfree;   /* 可用块数 */
  //   long f_bavail;  /* 非超级用户可获取的块数 */
  //   long f_files;   /* 文件结点总数 */
  //   long f_ffree;   /* 可用文件结点数 */
  //   fsid_t f_fsid;  /* 文件系统标识 */
  //   long f_namelen; /* 文件名的最大长度 */
  // };

  double total = diskInfo.f_blocks - diskInfo.f_bfree + diskInfo.f_bavail;
  if (total != 0.0) {
    // 使用的百分比
    double usage_rate =
        (diskInfo.f_blocks - diskInfo.f_bfree) * 100.0 / total + 1;
    return usage_rate;
  } else {
    return -1.0;
  }
}

bool SetThreadName(const std::string &name) {

  SPDLOG_INFO("set {}", name);

  pthread_setname_np(pthread_self(), name.c_str());

  return true;
}

std::string ReadProgressName(const std::string &pid) {
  std::string process_name_path = "/proc/" + pid + "/comm";

  std::string result(FileUtil::ReadFile(process_name_path));

  // 过滤换行符
  if (result.back() == 0x0A) {
    result.pop_back();
  }

  return result;
}

MemoryInformation GetProgressMemory(const std::string &pid) 
{
  MemoryInformation mem_info{};
  std::string process_name_address = "/proc/" + pid + "/statm";

  std::string str(FileUtil::ReadFile(process_name_address));
  sscanf(str.c_str(),"%u %u %u",&mem_info.total_memory,&mem_info.physical_memory,&mem_info.shared_memory);

  mem_info.total_memory = mem_info.total_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
  mem_info.physical_memory = mem_info.physical_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
  mem_info.shared_memory = mem_info.shared_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;

  return mem_info;
}

bool LinuxSystem(const char* cmd) {
  SPDLOG_INFO("cmd {}",cmd);

  pid_t status = ::system(cmd);
  if (status < 0) {
    SPDLOG_ERROR("error {}", strerror(errno));
    return false;
  } else {
    //status 低7bits表示致使任务退出的信号数值，右起第8bit表示是否生成coredump文件；
    //高8位是实际的exit参数值。宏的时间参见标注c头文件
    SPDLOG_INFO("exit status value = {}", status);

    //正确退出
    if (WIFEXITED(status)) {
      //操做成功
      if (0 == WEXITSTATUS(status)) {
        SPDLOG_INFO("run shell script successfully.\n");
        return true;
      } else { //操做失败
        SPDLOG_ERROR("run shell script fail, script exit code: {}", WEXITSTATUS(status));
        return false;
      }
    } else { //错误退出,这里属于信号中断了，WEXITSTATUS必定是0了
      SPDLOG_ERROR("exit status = {} {}", WEXITSTATUS(status),strerror(errno));
      return false;
    }
  }

  return false;
}

bool CheckIsMountPoint(const std::string& point)
{
  FILE *fp;
  char buffer[256];

  std::string cmd = "mountpoint " + point;
  fp = popen(cmd.c_str(), "r");
  if (!fp) {
    SPDLOG_ERROR("fp is null");
    return false;
  }
  fgets(buffer, sizeof(buffer), fp);
  pclose(fp);

  std::string res = buffer;

  SPDLOG_INFO("res {}", res);

  if (res.find("is a mountpoint") != std::string::npos) {
    SPDLOG_INFO("is a mountpoint");
    return true;
  } else {
    SPDLOG_INFO("is not a mountpoint");
    return false;
  }
}

std::string GetProgressPpid(const std::string &pid) 
{
  std::string file_address = "/proc/" + pid + "/stat";

  std::string str(FileUtil::ReadFile(file_address));

  int num = 0;
  std::string data;

  for (auto &c : str) {
    
    if (c == ' ') {
      ++num;
      if (num == 4) {
        break;
      }

      data.clear();
    } else {
      data.push_back(c);
    }
  }

  return data;
}

std::string GetLocalTime(void)    
{
  struct  timeb   stTimeb;
  struct tm * pTm;
  char psTime[128] = {0};

  ftime(&stTimeb);
  pTm = localtime(&stTimeb.time);

  /* 系统时间，格式: YYYYMMDDHHMMSS */
  sprintf(psTime, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
          pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
          pTm->tm_hour, pTm->tm_min, pTm->tm_sec, stTimeb.millitm);

  return std::string(psTime);   
}

void SetAllFdCloExec(void)
{
  static int old_fds[1024] = {0};
  pid_t pid = getpid();

  std::string path = "/proc/" + std::to_string(pid) + "/fd";

  SPDLOG_INFO("get path {}",path);
  std::vector<int> fds;

  if (!fs::is_directory(path)) {
    SPDLOG_WARN("{} is not a directory!", path);
    return;
  }

  for (auto &dir_entry : fs::directory_iterator(path)) {

    std::string fd = dir_entry.path().filename().string();
    if (fd[0] >= '1' && fd[0] <= '9') {
      SPDLOG_INFO("get fd {}",fd);
      int ifd = std::stoi(fd);

      // 如果为0则表明没有被设置过
      if (old_fds[ifd] == 0) {
        fds.push_back(ifd);
      }

      // 2表明是本次设置的
      old_fds[ifd] = 2;
    }
  }

  for (int i = 0; i < 1024;i++) {
    // 1是上次设置，但是本次没有搜索到，
    // 则可以认为该fd已经被close，重置
    // 为2的话就设置1
    if (old_fds[i] != 0) {
      old_fds[i]--;
    } 
  }

  for (auto fd : fds) {
    if (fd > 2) {
      SPDLOG_INFO("set fd {}",fd);
      fcntl(fd, F_SETFD, FD_CLOEXEC);
    } 
  }
}

bool GetTmpfsInfo(double* ratio,std::vector<std::string>* sub_info)
{
  // 获取/tmp目录大小
  fs::path tmp_path = "/tmp";
  auto info = fs::space(tmp_path);
  uintmax_t tmp_size = info.capacity - info.available;
  //SPDLOG_INFO("info {} {} {}",info.capacity, info.available,info.free);

  // 获取内存总大小
  std::ifstream meminfo("/proc/meminfo");
  if (!meminfo) {
    SPDLOG_ERROR("cant open /proc/meminfo");
    return false;
  }

  std::string line;
  unsigned long long total_mem_kb = 0;
  unsigned long long buffers = 0;
  unsigned long long cached = 0;
  unsigned long long sreclaimable = 0;
  while (std::getline(meminfo, line)) {
    if (line.compare(0, 9, "MemTotal:") == 0) {
      std::istringstream iss(line.substr(9));
      if (!(iss >> total_mem_kb)) {
        SPDLOG_ERROR("cant parse MemTotal");
        return false;
      }
    } else if (line.compare(0, 8, "Buffers:") == 0) {
      std::istringstream iss(line.substr(8));
      if (!(iss >> buffers)) {
        SPDLOG_ERROR("cant parse Buffers");
        return false;
      }
    } else if (line.compare(0, 7, "Cached:") == 0) {
      std::istringstream iss(line.substr(7));
      if (!(iss >> cached)) {
        SPDLOG_ERROR("cant parse Cached");
        return false;
      }
    } else if (line.compare(0, 13, "SReclaimable:") == 0) {
      std::istringstream iss(line.substr(13));
      if (!(iss >> sreclaimable)) {
        SPDLOG_ERROR("cant parse SReclaimable");
        return false;
      }
    }
  }

  meminfo.close();

  // 计算占比
  *ratio = static_cast<double>(tmp_size) / (total_mem_kb * 1024) * 100;

  if (*ratio < 20.0) {
    return true;
  }

  // 占比过高则记录
  char msg[1024] = {0};
  sprintf(msg,"Buffers/Cached/SReclaimable/All %llu/%llu/%llu/%llu bytes",buffers,cached,sreclaimable,buffers + cached + sreclaimable);
  sub_info->emplace_back(msg);

  std::error_code ec;
  // 统计/tmp目录下各个一级子目录与文件大小
  uintmax_t files_size = 0;

  if (!fs::is_directory(tmp_path)) {
    SPDLOG_WARN("{} is not a directory!", tmp_path);
    return false;
  }

  for (const auto& entry : fs::directory_iterator(tmp_path)) {
    struct stat st;
    if (stat(entry.path().c_str(), &st) == -1) {
      SPDLOG_ERROR("Failed to get stats for {}",entry.path().c_str());
      continue;
    }

    uintmax_t size = st.st_size;
    std::string type = "[f] ";

    // 如果是子目录则获取所有文件大小
    if (fs::is_directory(entry)) {
      type = "[d] ";
      for (const auto& subentry : fs::recursive_directory_iterator(entry.path())) {
        struct stat st;
        if (stat(subentry.path().c_str(), &st) == -1) {
          SPDLOG_ERROR("Failed to get stats for {}",subentry.path().c_str());
          continue;
        }

        size += st.st_size;
      }
    } 

    files_size += size;

    sub_info->emplace_back(type + entry.path().string() + " : " + std::to_string(size) + " bytes");
  }

  return true;
}

int32_t GetTakeOffStat(const std::shared_ptr<xag_nav::os::StorageBase>& storage_)
{
  rapidjson::Document doc;
  int32_t take_off_state = -1;
  if (storage_->getUserData("fc:SysData", doc) == 0) {
    if (!doc.IsNull() && 
        doc.HasMember("fc:SysData") && doc["fc:SysData"].IsObject()) {
      auto& sys_data = doc["fc:SysData"];

      if(sys_data.HasMember("TakeoffState") && sys_data["TakeoffState"].IsInt()) {
        take_off_state = sys_data["TakeoffState"].GetInt();
        SPDLOG_INFO("take_off_state {}",take_off_state);
      }
    }
  }

  return take_off_state;
}

}
}  // namespace utils
}  // namespace xos
}  // namespace xag
